
"""
Find matches for prefix.
  prefix to match stored in cgi name Prefix.
  put list of matches in env id Matches.
"""


# import must be absolute
from whiff.middleware import misc
from whiff import whiffenv
from whiff import resolver

class findWords(misc.utility):
    def __init__(self,
                 page,
                 limit=10,
                 wordFilePath="/usr/share/dict/words",
                 ):
        self.page = page
        self.limit = limit
        self.wordFilePath = wordFilePath
    def __call__(self, env, start_response):
        env = resolver.process_cgi(env, parse_cgi=True)
        print "top cgi dict", env.get(whiffenv.TOP_CGI_DICTIONARY)
        print "cgi prefix", env.get(whiffenv.CGI_PREFIX)
        print "full cgi prefix", env.get(whiffenv.FULL_CGI_PREFIX)
        print "cgi dict", env.get(whiffenv.CGI_DICTIONARY)
        prefix = whiffenv.cgiGet(env, "Prefix")
        print "looking for", repr(prefix)
        if prefix:
            limit = self.param_json(self.limit, env)
            wordFilePath = self.param_value(self.wordFilePath, env)
            matches = list(matchPrefix(prefix, limit, wordFilePath))
            if matches:
                env = env.copy()
                whiffenv.setId(env, "Matches", matches)
        return self.deliver_page(self.page, env, start_response)

def matchPrefix(prefix, limit=None, wordFilePath="/usr/share/dict/words"):
    f = file(wordFilePath)
    prefix = prefix.lower()
    # binary search to find word at prefix early insertion point in sorted file
    lowseek = 0
    f.seek(0, 2) # seek to eof
    highseek = f.tell()
    while lowseek<highseek:
        midseek = int((lowseek+highseek)/2)
        # back up to lowseek or just after newline
        while midseek>lowseek:
            beforemid = midseek-1
            f.seek(beforemid)
            ch = f.read(1)
            if ch=="\n":
                break
            midseek = beforemid
        # get the line at midseek
        f.seek(midseek)
        midline = f.readline().strip().lower()
        #pr "compare", (midseek, midline, prefix)
        if midline<prefix:
            #pr "too low"
            lowseek = f.tell()
        else:
            #pr "too high"
            highseek = midseek-1
    # yield prefix matches up to count
    f.seek(lowseek)
    count = 0
    while 1:
        yline = f.readline().strip().lower()
        if yline.startswith(prefix):
            yield yline
        else:
            #pr "break at nonprefix", repr(yline)
            break
        count += 1
        if limit is not None and count>=limit:
            #pr "break at limit", repr(limit)
            break

__middleware__ = findWords

# === testing stuff

def testpage(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [ str(whiffenv.getId(env, "Matches", "ERROR!!!")) ]

if __name__=="__main__":
    print "test match aaaa", list(matchPrefix("aaaa"))
    print "test match zzzz", list(matchPrefix("zzzz"))
    print "test match mmmmmm", list(matchPrefix("mmmmm"))
    print "test match gha", list(matchPrefix("gha"))
    print "test match wall", list(matchPrefix("wal"))
    print "test match aa", list(matchPrefix("aa"))
    env = { whiffenv.CGI_DICTIONARY : { "Prefix": ["pige"] } }
    f = findWords(testpage)
    print "test page", list( f(env, misc.ignore) )

